<?php

/*
 * This file is part of the Sylius package.
 *
 * (c) Sylius Sp. z o.o.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Tests\Sylius\Bundle\ApiBundle\Validator\Constraints;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sylius\Bundle\ApiBundle\Validator\Constraints\UniqueShopUserEmail;
use Sylius\Bundle\ApiBundle\Validator\Constraints\UniqueShopUserEmailValidator;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Canonicalizer\CanonicalizerInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class UniqueShopUserEmailValidatorTest extends TestCase
{
    private CanonicalizerInterface&MockObject $canonicalizer;

    private MockObject&UserRepositoryInterface $shopUserRepository;

    private ExecutionContextInterface&MockObject $executionContext;

    private UniqueShopUserEmailValidator $uniqueShopUserEmailValidator;

    protected function setUp(): void
    {
        parent::setUp();
        $this->canonicalizer = $this->createMock(CanonicalizerInterface::class);
        $this->shopUserRepository = $this->createMock(UserRepositoryInterface::class);
        $this->executionContext = $this->createMock(ExecutionContextInterface::class);
        $this->uniqueShopUserEmailValidator = new UniqueShopUserEmailValidator($this->canonicalizer, $this->shopUserRepository);
        $this->uniqueShopUserEmailValidator->initialize($this->executionContext);
    }

    public function testAConstraintValidator(): void
    {
        self::assertInstanceOf(ConstraintValidatorInterface::class, $this->uniqueShopUserEmailValidator);
    }

    public function testDoesNothingIfValueIsNull(): void
    {
        $this->executionContext->expects(self::never())->method('addViolation')->with($this->any());
        $this->uniqueShopUserEmailValidator->validate(null, new UniqueShopUserEmail());
    }

    public function testThrowsAnExceptionIfConstraintIsNotOfExpectedType(): void
    {
        self::expectException(\InvalidArgumentException::class);
        $this->uniqueShopUserEmailValidator->validate(
            '',
            $this->createMock(Constraint::class),
        );
    }

    public function testDoesNotAddViolationIfAUserWithGivenEmailIsNotFound(): void
    {
        $this->canonicalizer->expects(self::once())->method('canonicalize')->with('eMaIl@example.com')->willReturn('email@example.com');
        $this->shopUserRepository->expects(self::once())->method('findOneByEmail')->with('email@example.com')->willReturn(null);
        $this->executionContext->expects(self::never())->method('addViolation')->with($this->any());
        $this->uniqueShopUserEmailValidator->validate('eMaIl@example.com', new UniqueShopUserEmail());
    }

    public function testAddsViolationIfAUserWithGivenEmailIsFound(): void
    {
        /** @var ShopUserInterface|MockObject $shopUserMock */
        $shopUserMock = $this->createMock(ShopUserInterface::class);
        $constraint = new UniqueShopUserEmail();

        $this->canonicalizer->expects(self::once())
            ->method('canonicalize')
            ->with('eMaIl@example.com')
            ->willReturn('email@example.com');

        $this->shopUserRepository->expects(self::once())
            ->method('findOneByEmail')
            ->with('email@example.com')
            ->willReturn($shopUserMock);

        $this->executionContext->expects(self::once())
            ->method('addViolation')
            ->with('sylius.user.email.unique');

        $this->uniqueShopUserEmailValidator->validate('eMaIl@example.com', $constraint);
    }
}
